#include "stdafx.h"
#include "TabBars.h"
#include "ShellTools.h"
#include "TabBarWnd.h"
#include "TabManagerWnd.h"
#include "DevStudioWnd.h"
#include "Commands.h"
#include "DSWindow.h"
#include "DS_MDIWnd.h"
#include "DSAddin.h"
#include "BCMenu.h"        
#include "Globals.h"
#include "ShellContextMenu.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define ID_TAB          1000

//#define MENU_WIDTH      (14 + 8)       // width for menu button
//#define MENU_HEIGHT     (14 + 8)       // height for menu button

#define GRIPPER_SIZE    6             // short side


// flags for popup menus
#define TPM_FLAGS   (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD)

CTabBarsWnd *pGlobalTabs = NULL;

/////////////////////////////////////////////////////////////////////////////
// CTabBarsWnd

//##ModelId=431C34C70187
CTabBarsWnd::CTabBarsWnd(CCommands* pCommands) : m_RectGripper(0, 0, 0, 0), m_iLockUpdates(0)
{
  m_pCommands    = pCommands;
  m_pApplication = pCommands->GetApplicationObject();
	m_uTimerID = 0;
	m_pFont = NULL;
}

//##ModelId=431C34C70281
CTabBarsWnd::~CTabBarsWnd()
{
	POSITION p = m_TabData.GetStartPosition();
	tabdata_t *pData;
	HWND key;

	while (p)
	{
		m_TabData.GetNextAssoc(p, key, pData);
		delete pData;
	}
	
	if(m_pFont != NULL)
	{
		m_pFont->DeleteObject();
		delete m_pFont;
	}

	DestroyWindow();
}

BEGIN_MESSAGE_MAP(CTabBarsWnd, CWnd)
	//{{AFX_MSG_MAP(CTabBarsWnd)
	ON_WM_CREATE()
	ON_WM_WINDOWPOSCHANGED()
	ON_WM_DESTROY()
	ON_WM_PAINT()
	ON_WM_DRAWITEM()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	ON_NOTIFY(TCN_SELCHANGE, ID_TAB,  OnSelchangeTabCtrl)
	ON_NOTIFY(NM_RCLICK,ID_TAB,  OnRClickTabCtrl)
	ON_NOTIFY(TTN_NEEDTEXT,  0,       OnToolTipTabCtrl)
	ON_WM_MEASUREITEM()
	ON_MESSAGE(MSG_TABCTRL_2_TABBARWND,OnDbClickTabCtrl)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTabBarsWnd message handlers

//##ModelId=431C34C702FE
int CTabBarsWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
  if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;
  
	m_TabImages.Create(16, 16, ILC_COLOR8|ILC_MASK, 10, 5);
	DWORD dwSty = WS_CHILD|WS_VISIBLE|TCS_TOOLTIPS|TCS_SINGLELINE|TCS_FORCELABELLEFT|TCS_BUTTONS|TCS_FLATBUTTONS|TCS_RAGGEDRIGHT|TCS_FOCUSNEVER;
  if(cfg_iOrientation == soBottom)
      dwSty |= TCS_BOTTOM;

  SetRedraw(FALSE);

	m_TabCtrl.Create(dwSty, CRect(0,0,1,1), this, ID_TAB);
	DWORD dwExStyle= m_TabCtrl.GetExtendedStyle();
	m_TabCtrl.SetExtendedStyle(dwExStyle  | TCS_EX_FLATSEPARATORS);


	  // use a font smaller than default
	CFont *pFont = new CFont();
	pFont->CreatePointFont(80,_T("MS Sans Serif"));
	SetTabFont(pFont);
	m_TabCtrl.SetPadding(CSize(2,2));
  m_TabCtrl.SetFont(pFont, TRUE);
  
	UpdateTabs();
  SetRedraw(TRUE);
  RedrawWindow();

	m_TabCtrl.SetImageList(&m_TabImages);

	m_uTimerID = SetTimer(TABBAR_WINDOW_TIMERID,10000L,NULL);//for auto save
	return 0;
}

//##ModelId=431C34C8007D
void CTabBarsWnd::OnDestroy() 
{
	CWnd::OnDestroy();
}

//##ModelId=431C34C8001F
void CTabBarsWnd::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) 
{
  CWnd::OnWindowPosChanged(lpwndpos);

  if (pGlobalActiveDSWindow->GetSaferHwnd())
  {
    static CWnd *MDIAreaWnd = NULL;
    if (!MDIAreaWnd)
    {
			MDIAreaWnd = pGlobalActiveDSWindow->GetParent();
			ASSERT(MDIAreaWnd);
    }
    SetWindowPos(MDIAreaWnd->GetWindow(GW_HWNDPREV), 0, 0, 0, 0,SWP_NOMOVE | SWP_NOSIZE);
  }

  if ((lpwndpos->flags & (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER)) == (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER))
    return;

  CRect r, r2, r3;
  GetClientRect(r);
  g_pDevStudioWnd->GetClientRect(r2);
  GetWindowRect(r3);
  g_pDevStudioWnd->ScreenToClient(r3);

  if (r3.left != 0)
  {
    r.left++;
  }

  if (r3.right != r2.right)
  {
    r.right--;
  }
  // using global var's isn't great software eng. - but it works :)
  if (pGlobalActiveManager->GetSafeHwnd())
  {
    r.DeflateRect(pGlobalActiveManager->GetBorder());
  }
  
	r.left += GRIPPER_SIZE;
	
	if(cfg_iOrientation == soTop)
	{
		r.top += 1;
	}
	else
	{
		r.bottom -= 1;
	}
  
	m_TabCtrl.MoveWindow(r);
  m_TabCtrl.ShowWindow(m_TabCtrl.GetItemCount()? SW_SHOW : SW_HIDE);

  m_RectGripper = CRect(r.left - GRIPPER_SIZE, r.top, r.left, r.bottom);
}

// The following event handler is called when the user presses on a tab.
// The tab's corresponding window is activated.
// See FindDSWindow() for DevStudio window enumeration techniques.
//##ModelId=431C34C800BC
void CTabBarsWnd::OnSelchangeTabCtrl(NMHDR* pNMHDR, LRESULT* pResult) 
{
    CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow;

    if (FindDSWindow(m_TabCtrl.GetCurSel(), pWindow))
    {
        ActivateWindow(pWindow);
    }
}

// the following event handler is called when the user right-clicks on a tab.
//##ModelId=431C34C80119
void CTabBarsWnd::OnRClickTabCtrl(NMHDR* pNMHDR, LRESULT* pResult) 
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState());

  CComPtr<IDispatch>                                  pDisp;
  CComQIPtr<IGenericWindow, &IID_IGenericWindow>      pWindow;//
  CComQIPtr<IGenericDocument, &IID_IGenericDocument>  pDoc;
              
  BCMenu          menu;
  BCMenu *        pPopup;
  POINT           point;
  TCHITTESTINFO   hti;
  int             iTab;
  
  hti.pt = CPoint(GetMessagePos());
  m_TabCtrl.ScreenToClient(&hti.pt);
  iTab = m_TabCtrl.HitTest(&hti);

  if (iTab < 0)
    return;
      
  if(!FindDSWindow(iTab, pWindow))
    return;

  VERIFY_OK(m_pApplication->EnableModeless(VARIANT_FALSE));

  if (LoadPopupMenu(IDR_POPUP, menu, pPopup))
  {    
    CComBSTR        bStr;
    CString         cStr, cFullPath;
    DsSaveStatus    iSaved;
    bool            bIsTextWindow, bHasDoc;
    int             i;

    typedef struct 
    { 
      UINT cmd; //command code
      bool bDocWindowsOnly; //just effectively in document windows
      bool bActiveWindowsOnly;//just effectively in generic windows
    } cmnd_t;

    // special commands which I want it show in menu
    static cmnd_t commands[] =
    {
      {IDC_HEADERFLIP, true,  false}, 
      {IDC_SAVE,       true,  false},
      {IDC_PROPERTIES, false, true},
      {IDC_OPENASTEXT, true,  false},
      {IDC_ROATTR,     true,  false}
    };

    GetCursorPos(&point);

    pWindow->get_Type(&bStr);
    cStr = bStr;
    bIsTextWindow = bool(cStr == "Text");

    bHasDoc = GetDocumentObject(pWindow, pDoc);

    if (bHasDoc)
    {
      pDoc->get_FullName(&bStr);
      cFullPath = bStr;
    }
    else
    {
      cFullPath.Empty();
    }

    if (bIsTextWindow)
    {
      pPopup->EnableMenuItem(IDC_OPENASTEXT, MF_GRAYED | MF_BYCOMMAND);
    }

    CShellContextMenu scm(::GetActiveWindow(), cFullPath);
    const int iShellContextPos = 2;//pPopup->GetMenuItemCount() - 3;
    pPopup->ModifyODMenu(NULL, "Shell", IDB_SHELLCONTEXT);
    if ((cfg_bNoShellContext  &&  !(GetKeyState(cfg_iShellKey) & 0x8000)) || !bHasDoc)
    {
      pPopup->EnableMenuItem(iShellContextPos, 
          MF_GRAYED | MF_BYPOSITION);
    }
    else
    {
			scm.SetMenu((BCMenu *)pPopup->GetSubMenu(iShellContextPos),pPopup);
    }
    ::SetMenuDefaultItem(pPopup->m_hMenu, 0, TRUE);

		for (i = 0; i < countof(commands); i++)
		{
			if (!bHasDoc  &&  commands[i].bDocWindowsOnly)
			{
				pPopup->EnableMenuItem(commands[i].cmd,MF_GRAYED | MF_BYCOMMAND);
			}
		}

		int iCmd;
		if (GetKeyState(cfg_iShellKey) & 0x8000)
		{
			iCmd = pPopup->GetSubMenu(iShellContextPos)->TrackPopupMenu(TPM_FLAGS, point.x, point.y, this);
		}
		else
		{
			iCmd = pPopup->TrackPopupMenu(TPM_FLAGS, point.x, point.y, this);
		}

    if (scm.IsMenuCommand(iCmd))
    {
      scm.InvokeCommand(iCmd);
    }
    else
    {
      for (i = 0; i < countof(commands); i++)
      {
        if (iCmd == (int)commands[i].cmd)
        {
          if (commands[i].bActiveWindowsOnly)
          {
            VARIANT_BOOL vb;
            pWindow->get_Active(&vb);
            if (vb == VARIANT_TRUE)
            {
              pWindow->put_Active(VARIANT_FALSE);
            }
            pWindow->put_Active(VARIANT_TRUE);
          }
          break;
        }
      }

      HWND hWnd, hWndNext, hWndExcept = NULL;

      switch (iCmd)
      {
        case IDC_SELECT:
          ActivateWindow(pWindow);
          break;
        case IDC_CLOSE:
        {
					CloseDSWindow(pWindow);
/*
          VARIANT_BOOL vb;
          // bStr <- name of window to close
          // (used later to retrieve window handle)
          pWindow->get_Caption(&bStr);
          // are we closing the active window?
          pWindow->get_Active(&vb);
          if (vb == VARIANT_FALSE)
          {
            // not closing the active window, so save the active
            //  window in pWindow
            m_pApplication->get_ActiveWindow(&pDisp);
            pWindow = pDisp;
						pDisp = NULL;
            m_iLockUpdates++;
            // force an event:
            pWindow->put_Active(VARIANT_FALSE);
          }

          ::SendMessage(GetHWND(bStr), WM_CLOSE, 0, 0);//now close it!

          // need to reactivate the previously active window?
          if (vb == VARIANT_FALSE)
          {
            // get HWND of previously active window
            HWND hWnd = GetHWND(pWindow);
            ::PostMessage(::GetParent(hWnd), WM_MDIACTIVATE,(WPARAM)hWnd, 0);
          }
*/
          break;
        }

        case IDC_CLOSEALLEXCEPT:
          hWndExcept = GetHWND(pWindow);
          hWnd = ::GetTopWindow(pGlobalMDIManager->m_hWnd);
          while (hWnd)
          {
            hWndNext = ::GetNextWindow(hWnd, GW_HWNDNEXT);
            if (hWnd != hWndExcept)
                ::PostMessage(hWnd, WM_CLOSE, 0, 0);
            hWnd = hWndNext;
          }
          ActivateWindow(pWindow,FALSE);
          ActivateWindow(pWindow);
					break;
        case IDC_CLOSEALL:
          // doesn't use ExecuteCommand("WindowCloseAll") 
          // (see above)
          hWnd = ::GetTopWindow(pGlobalMDIManager->m_hWnd);
          while (hWnd)
          {
            hWndNext = ::GetNextWindow(hWnd, GW_HWNDNEXT);
            ::PostMessage(hWnd, WM_CLOSE, 0, 0);
            hWnd = hWndNext;
          }
          break;
        case IDC_SAVE:
        {
          CComVariant varPrompt = true, v = lpszNull;
          pDoc->Save(v, varPrompt, &iSaved);
          break;
        }
        case IDC_SAVEALL:
        {
            bStr = _T("FileSaveAll");
            m_pApplication->ExecuteCommand(bStr);
            break;
        }

          // BUG BUG: SaveAs doesn't work at all through 
          //  IApplication->ExecuteCommand().  You can see this clearly 
          //  by using the sample "command window" add-in that comes 
          //  with VC.

/*              case IDC_SAVEAS:
          {
              bStr = _T("FileSaveAs");
              m_pApplication->ExecuteCommand(bStr);
              break;
          }
*/
        case IDC_PROPERTIES:
        {
          bStr = _T("Properties");
          m_pApplication->ExecuteCommand(bStr);
          break;
        }
        case IDC_HEADERFLIP:
        {
          m_pCommands->DoHeaderFlip(cFullPath);
          break;
        }
        case IDC_OPENASTEXT:
        {
          m_pCommands->DoOpenAsText(cFullPath);
          break;
        }
        case IDC_SETCURRDIR:
        {
          m_pCommands->DoSetCurrDir(cFullPath);
          break;
        }

        case IDC_ROATTR:
        {
					DWORD attr;
					if (!cFullPath.IsEmpty())
					{
						attr = GetFileAttributes(cFullPath);
						SetFileAttributes(cFullPath,attr ^ FILE_ATTRIBUTE_READONLY);
					}
					break;
        }
      }
    }
  }
  
  VERIFY_OK(m_pApplication->EnableModeless(VARIANT_TRUE));
}

//##ModelId=431C34C80159
void CTabBarsWnd::OnDbClickTabCtrl(int nTabIdx,LPARAM lParam)
{
  CComQIPtr<IGenericWindow, &IID_IGenericWindow>      pWindow;//

  if (nTabIdx < 0)
    return;

  if(!FindDSWindow(nTabIdx, pWindow))
    return;

	CloseDSWindow(pWindow);
}

// The tool tip handler displays the full path of a document.
// All open documents are scanned, looking for a the respective window's
// document.  Once the document is found, the full path is extracted.
// This function demonstrates enumeration of both documents and windows.
//##ModelId=431C34C800EA
void CTabBarsWnd::OnToolTipTabCtrl(NMHDR* pNMHDR, LRESULT* pResult) 
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    LPTOOLTIPTEXT   lpttt = (LPTOOLTIPTEXT)pNMHDR;
    TCHITTESTINFO   hti;
    int             iTab;
    static CString  cStr; // safe - there can be only one tip at a time

    *pResult = 0;
/*
    if (lpttt->hdr.idFrom == IDC_MENU)
    {
        cStr.LoadString(IDS_MENU_TT);
    }
    else
*/    
	{
        hti.pt = CPoint(GetMessagePos());
        m_TabCtrl.ScreenToClient(&hti.pt);
        iTab = m_TabCtrl.HitTest(&hti);

        if (iTab >= 0)
        {
            CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow;
            CComQIPtr<IGenericDocument, &IID_IGenericDocument> pDoc;
            CComBSTR bstrCaption;

            FindDSWindow(iTab, pWindow);
            if (GetDocumentObject(pWindow, pDoc))
            {
                pDoc->get_FullName(&bstrCaption);
                cStr = bstrCaption;

                if (cStr.IsEmpty())
                {
                    // no name?  it's a text editor without an associated 
                    // file
                    cStr.LoadString(IDS_UNSAVED_TT);
                }
            }
            else
            {
                cStr.LoadString(IDS_DSWIN_TT);
            }
        }
    }
    // variation on original fix by Christian Rodemeyer 
    // (Christian.Rodemeyer@t-online.de): use lpszText instead of szText so 
    // strings over 80 chars will be handled properly.
    lpttt->lpszText = LPTSTR(LPCTSTR(cStr));//it drive orbit faint!
}


/////////////////////////////////////////////////////////////////////////////
// CTabBarsWnd commands


// Updates the tabs by enumerating DevStudio's windows
// This method demonstrates enumeration using the "Item" property.
//##ModelId=431C34C70232
void CTabBarsWnd::UpdateTabs()
{
	long                cWindows;
	HWND                hWnd, hWndActive;
	BSTR                bStr;
	CString             cStr, cPath, cFile, cOther;
	CComVariant         index;
	CComPtr<IDispatch>  pDisp;
	HICON               hIcon;
	CTypedPtrArray      <CPtrArray, tabdata_t *> TabList;
	CTypedPtrList       <CPtrList,  tabdata_t *> TabsNotInList;
	tabdata_t           *pData, *pOldData;
	POSITION            p;
	int                 iIndex, iTabListCount = 0;
	BOOL                bHasAsterisk, bIsRC;
    
	if (!this)
		return;

	if (m_iLockUpdates)
	{
		m_iLockUpdates--;
		return;
	}

	hWndActive = pGlobalActiveDSWindow->GetSaferHwnd();

	CComQIPtr<IWindows, &IID_IWindows> pWindows;
	m_pApplication->get_Windows(&pDisp);
	pWindows = pDisp;
	pDisp = NULL;

	pWindows->get_Count(&cWindows);

  // set verified flag to FALSE for all items
  p = m_TabData.GetStartPosition();
  while (p)
  {
		m_TabData.GetNextAssoc(p, hWnd, pData);
		if (pData)
		{
			pData->bVerified = FALSE;
		}
  }

  // original addition by: Anatoly Sennov [a_s@softhome.net]
  // list all windows that potentially could be closed
  if( cfg_bUseWindowLimit ) 
	{
		CTypedPtrArray <CPtrArray, CString *> CloseList;
		int cIgnored = 0;
		for (int i = 0; i < cWindows; i++) 
		{
			index = i + 1;
			CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow;
			pWindows->Item(index, &pDisp);
			pWindow = pDisp;
			pDisp = NULL;
  
			pWindow->get_Caption(&bStr);
			cPath = bStr;

			// windows which have been modified should not be closed
			if( cPath.Right(2) != " *" ) 
			{
				CString * record = new CString(cPath);
				CloseList.InsertAt(0, record);
			}
			else
					cIgnored++;
		}
		int windowsToClose = min(cWindows - (long)cfg_iWindowLimit,CloseList.GetSize());

		// make sure user can open at least one window when all the documents
		// have been modified
		while (windowsToClose && (cWindows - windowsToClose - cIgnored < 1))
		{
			windowsToClose--;
		}

		// close them!
		for(i = 0; i < windowsToClose; i++ ) 
		{
			if((hWnd = GetHWND(*CloseList[i])) != NULL) 
			{
				m_iLockUpdates++;
				::SendMessage(hWnd, WM_CLOSE, 0, 0);
				cWindows--;
			}
		}

		// cleanup
		for( i = 0; i < CloseList.GetSize(); i++)
				delete CloseList[i];
		CloseList.RemoveAll(); 
  }

  // enumerate the windows
  for (int i = 0; i < cWindows; i++)
  {
    index = i + 1;
    CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow;
    pWindows->Item(index, &pDisp);
    pWindow = pDisp;
    pDisp = NULL;

    pWindow->get_Caption(&bStr);
    cPath = bStr;

    if ((hWnd = GetHWND(cPath)) == NULL)
        return;
    pData = m_TabData[hWnd];
    if (pData)
    {
//            if (!cfg_bPreserveOrder) 
//				pData->iNumber = i;
      TabList.SetAtGrow(pData->iIndex, pData);
      {
          iTabListCount++;
      }
      m_TabData[hWnd] = NULL;
    }
    else
    {
      pData = new tabdata_t;
      pData->hWnd = hWnd;
//            if (cfg_bPreserveOrder)
//           {
      TabsNotInList.AddTail(pData);
//            }
//            else
//            {
//                pData->iNumber = i;
//                TabList.SetAtGrow(i, pData);
//                iTabListCount++;
//            }
    }

    // if the user modified the document, there will be a asterisk
    // appended to the window title (removed if the file was just
    // saved), so we'll make sure we have the latest title:
    pData->cPath = cPath;

    // mark this tab so as not to delete it
    pData->bVerified = TRUE;    
  }

  m_TabCtrl.SetRedraw(FALSE);

  if (ImageList_GetImageCount(m_TabImages) < cWindows)
  {    
		m_TabCtrl.SetImageList(NULL);
		ImageList_SetImageCount(m_TabImages, cWindows + 5);
		m_TabCtrl.SetImageList(&m_TabImages);
  }
  
  // items with their verified flag set to FALSE should be deleted
  p = m_TabData.GetStartPosition();
  while (p)
  {
    m_TabData.GetNextAssoc(p, hWnd, pData);
    if (!pData  ||  !pData->bVerified)
    {
      m_TabData.RemoveKey(hWnd);
      delete pData;
    }
  }
  // create the actual tabs
  TC_ITEM     newTabItem, oldTabItem;
  int         iCurrTabIndex  = 0;    // the index of the inserted tab
  
  iIndex = 0;
  newTabItem.mask = TCIF_PARAM | TCIF_TEXT;
  oldTabItem.mask = TCIF_PARAM;

  while (1)
  {
    p = TabsNotInList.GetHeadPosition();
    if (!iTabListCount || (cfg_iAddOrder == aoFront && p))
    {
      if(!p) 
				break;
      pData = TabsNotInList.GetAt(p);
      TabsNotInList.RemoveAt(p);

      if (!iTabListCount)
      {
        pData->iIndex = iCurrTabIndex;
      }
    }
    else
    {
      iTabListCount--;
      do
      {
        pData = TabList[iIndex++];
      } while (!pData);
    }

    cStr = pData->cPath;
 		//dfgthsthswtr
//		iCurrTabNumber = pData->iNumber;
    
		if (cStr.Right(2) == " *")
    {
      cStr = cStr.Left(cStr.GetLength() - 2);
      bHasAsterisk = TRUE;
    }
    else
    {
      bHasAsterisk = FALSE;
    }

  // process .rc files
    CString cStrRC = cStr;
    cStrRC.MakeUpper();
    int nPos, nRight;
    if (cStrRC.Find(".RC") >= 0  &&  cStrRC.Find(" - ") >= 0)
    {
      bIsRC = TRUE;

      if (!cfg_bDispRID)
      {
        nPos   = cStr.Find("- ");
        cStrRC = cStr.Left(nPos + 2);
        cStr   = cStr.Mid(nPos + 2);
        nPos   = cStr.Find(' ');
        cStr   = cStrRC + cStr.Mid(nPos + 1);
      }

      if (!cfg_bDispResType)
      {
        nPos = cStr.ReverseFind('(');
        cStr = cStr.Left(nPos);
      }

      if (!cfg_bDispResLang)
      {
        nPos   = cStr.Find('[');
        nRight = cStr.Find(']');

        // fix by Mark Cooke [mark.cooke@oxinst.co.uk]:
        // DevStudio cal ommit the language part, so check for this
        if( (0 <= nPos)  &&  (0 <= nRight) )
        {
          cStrRC = cStr.Mid(nRight + 2);
          cStr   = cStr.Left(nPos);
          cStr  += cStrRC;
        }
      }

      nPos = cStr.Find(" - ");
      if (!cfg_bDispRC)
      {
        cOther = cStr.Mid(nPos + 2);
        cFile = "";
      }
      else
      {
        cFile = cStr.Left(nPos);
        if (nPos == cStr.GetLength() - 3)
        {
	        cOther = "";
        }
        else
        {
          cOther = cStr.Mid(nPos);
        }
      }
    }
    else
    {
      bIsRC = FALSE;
      cFile = cStr;
      cOther = "";
    }

    hIcon = (HICON)GetClassLong(pData->hWnd, GCL_HICONSM);
      
    if (cfg_bShowIcons  &&  hIcon)
    {
      newTabItem.mask |= TCIF_IMAGE;
      m_TabImages.Replace(iCurrTabIndex, hIcon);
      newTabItem.iImage = iCurrTabIndex;
    }
    else
    {
      newTabItem.mask &= ~TCIF_IMAGE;
    }

    if (bHasAsterisk)
    {
      cStr += " *";
    }

    newTabItem.pszText = cStr.GetBuffer(0);
    newTabItem.lParam = (LPARAM)(pData);

    pOldData = m_TabData[pData->hWnd];
    if (pOldData  &&  pOldData != pData)
      delete pOldData;
    m_TabData[pData->hWnd] = pData;
                                    
    pData->iIndex = iCurrTabIndex;

      // do our best to just replace the text (avoid flicker)
    do
    {
      if (m_TabCtrl.GetItem(iCurrTabIndex, &oldTabItem) == FALSE)
      {
        oldTabItem.lParam = NULL;
        break;
      }

      if (((tabdata_t *)(oldTabItem.lParam))->hWnd == pData->hWnd)
        break;

      if (m_TabCtrl.GetItemCount() <= cWindows)
      {
        oldTabItem.lParam = NULL;
        break;
      }

      m_TabCtrl.DeleteItem(iCurrTabIndex);
    } while (1);

    if ((HWND)oldTabItem.lParam != NULL)
    {
      m_TabCtrl.SetItem(iCurrTabIndex, &newTabItem);
    }
    else
    {
      m_TabCtrl.InsertItem(iCurrTabIndex, &newTabItem);
    }

    if (pData->hWnd == hWndActive)
    {
      m_TabCtrl.SetCurSel(iCurrTabIndex);
    }

    iCurrTabIndex++;
  }

  while (m_TabCtrl.GetItemCount() > cWindows)
  {
		m_TabCtrl.DeleteItem(m_TabCtrl.GetItemCount() - 1);
  }

  m_TabCtrl.SetRedraw(TRUE);
  m_TabCtrl.RedrawWindow();
}


// Retrieves the pointer to a DevStudio window that corresponds to a tab,
// by scanning the DevStudio document windows, looking for a window with the
// same title as the tab.
// This method demonstrates enumeration using the enumeration 
// function/property.
//##ModelId=431C34C702C0
bool CTabBarsWnd::FindDSWindow(int iSel, CComQIPtr<IGenericWindow, &IID_IGenericWindow>& pWindow)
{
    HWND hWnd = GetTabData(iSel)->hWnd;
    
    CComPtr<IDispatch> pDisp;
    CComQIPtr<IWindows, &IID_IWindows> pWindows;

    m_pApplication->get_Windows(&pDisp);
    pWindows = pDisp;
    pDisp = NULL;

    CComPtr<IUnknown> pUnk;
    CComQIPtr<IEnumVARIANT, &IID_IEnumVARIANT> pNewEnum;
    if (SUCCEEDED(pWindows->get__NewEnum(&pUnk)) && pUnk != NULL)
    {
        pNewEnum = pUnk;
        VARIANT varWindow;
        CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindowItem;
        while (pNewEnum->Next(1, &varWindow, NULL) == S_OK)
        {
            ASSERT (varWindow.vt == VT_DISPATCH);
            pWindowItem = varWindow.pdispVal;
            VariantClear(&varWindow);
            if (GetHWND(pWindowItem) == hWnd)
            {
                pWindow = pWindowItem;
                return TRUE;
            }
        }
    }

    return FALSE;
}


//##ModelId=431C34C7029F
BOOL CTabBarsWnd::LoadPopupMenu(int nResourceID, BCMenu& menu,BCMenu *& pPopup)
{
  if (menu.LoadMenu(nResourceID))
  {
    pPopup = (BCMenu *)menu.GetSubMenu(0);
//        pPopup->LoadToolbar(IDR_ICONS);
    pPopup->LoadToolbar(IDR_TOOLBAR_MEDIUM);
    return TRUE;
  }

  return FALSE;
}

//##ModelId=431C34C702EE
void CTabBarsWnd::CloseDSWindow(CComQIPtr<IGenericWindow, &IID_IGenericWindow>& pWindow)
{
  CComPtr<IDispatch> pDisp;
	CComBSTR bStr;
  VARIANT_BOOL vb;
  // bStr <- name of window to close
  // (used later to retrieve window handle)
  pWindow->get_Caption(&bStr);
  // are we closing the active window?
  pWindow->get_Active(&vb);
  if (vb == VARIANT_FALSE)
  {
    // not closing the active window, so save the active
    //  window in pWindow
    m_pApplication->get_ActiveWindow(&pDisp);
    pWindow = pDisp;
		pDisp = NULL;
    m_iLockUpdates++;
    // force an event:
    pWindow->put_Active(VARIANT_FALSE);
  }

  ::SendMessage(GetHWND(bStr), WM_CLOSE, 0, 0);//now close it!

  // need to reactivate the previously active window?
  if (vb == VARIANT_FALSE)
  {
    // get HWND of previously active window
    HWND hWnd = GetHWND(pWindow);
    ::PostMessage(::GetParent(hWnd), WM_MDIACTIVATE,(WPARAM)hWnd, 0);
  }
}

//##ModelId=431C34C801B5
CTabBarsWnd::tabdata_t * CTabBarsWnd::GetTabData(int iTabIndex)
{
  TC_ITEM tab_item;

  tab_item.mask = TCIF_PARAM;
  m_TabCtrl.GetItem(iTabIndex, &tab_item);    

  return ((tabdata_t *)tab_item.lParam);
}

//##ModelId=431C34C70262
void CTabBarsWnd::ActivateTab(int iIndex)
{
  CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow;
  tabdata_t * pData;

  // find the tab to select
  for (int i = 0; i < m_TabCtrl.GetItemCount(); i++)
  {
    pData = GetTabData(i);
    if (pData->iIndex == iIndex)
    {
      if (FindDSWindow(i, pWindow))
      {
        pWindow->put_Active(VARIANT_TRUE);
      }
      break;
    }
  }
}

// GetDocumentObject: Retrieves a window's document object.
//
// BUG BUG: DevStudio provides the get_Parent that returns a window's 
// document object.  But using get_Parent on a non-text window, might 
// cause DevStudio to crash (a good example of this is a resource window).  
// Since such windows might actually have a document, this function is used
// to get document "the hard way"
//##ModelId=431C34C70242
bool CTabBarsWnd::GetDocumentObject(
    CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow,
    CComQIPtr<IGenericDocument, &IID_IGenericDocument>& pDoc)
{
  // Matching the document to a window is a two-step process:
  //  1. get a document object from the document list
  //  2. scan all windows belonging to the document
  //  All of the documents/document windows are scanned until the
  //  window is matched.
  CComPtr<IUnknown> pUnk;
  CComPtr<IDispatch> pDisp;
  CComQIPtr<IDocuments, &IID_IDocuments> pDocuments;
  CComQIPtr<IEnumVARIANT, &IID_IEnumVARIANT> pNewEnum;

  m_pApplication->get_Documents(&pDisp);
  pDocuments = pDisp;
  pDisp = NULL;

  if (SUCCEEDED(pDocuments->get__NewEnum(&pUnk)) && pUnk != NULL)
  {
    pNewEnum = pUnk;
    VARIANT varDoc;
    while (pNewEnum->Next(1, &varDoc, NULL) == S_OK)
    {
      ASSERT (varDoc.vt == VT_DISPATCH);
      pDoc = varDoc.pdispVal;
      VariantClear(&varDoc);
      
      // iterate the document's windows
      CComQIPtr<IWindows, &IID_IWindows> pWindows;
      pDoc->get_Windows(&pDisp);
      pWindows = pDisp;
      pDisp = NULL;
      
      CComPtr<IUnknown> pUnk2;
      CComQIPtr<IEnumVARIANT, &IID_IEnumVARIANT> pEnum2;
      if (SUCCEEDED(pWindows->get__NewEnum(&pUnk2)) && pUnk2 != NULL)
      {
        pEnum2 = pUnk2;
        VARIANT varWindow;
        CComQIPtr<IGenericWindow, &IID_IGenericWindow> pWindow2;
        while (pEnum2->Next(1, &varWindow, NULL) == S_OK)
        {
          ASSERT (varWindow.vt == VT_DISPATCH);
          pWindow2 = varWindow.pdispVal;
          VariantClear(&varWindow);
          if (pWindow2 == pWindow)
          {
            return true;
          }
        }
      }
    }
  }
  return false;
}

//##ModelId=431C34C8009C
void CTabBarsWnd::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

  CRect   r, rcClient;
	
	GetClientRect(rcClient);

	CDC memDC;
	CBitmap bitmap;
	memDC.CreateCompatibleDC(&dc);
	bitmap.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
	CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);

	memDC.SetBkMode( TRANSPARENT );
//	memDC.FillSolidRect(&rcClient, m_clrBtnFace );
	
	// First let the control do its default drawing.
	CWnd::DefWindowProc( WM_PAINT, (WPARAM)memDC.m_hDC, 0 );
	
  COLORREF m_clrBtnShadow = ::GetSysColor(COLOR_BTNSHADOW); 
  COLORREF m_clrBtnHilite = ::GetSysColor(COLOR_BTNHIGHLIGHT);
  COLORREF m_clrBtnFace   = ::GetSysColor(COLOR_BTNFACE);
	
	memDC.FillSolidRect(&rcClient, m_clrBtnFace );
  memDC.Draw3dRect(&rcClient, m_clrBtnHilite, m_clrBtnShadow); 
  
	r = m_RectGripper;
  r.left++;
  r.right = r.left + GRIPPER_SIZE;
  r.DeflateRect(0, 1);

  memDC.Draw3dRect(r.left, r.top, 3, r.Height(), m_clrBtnHilite, m_clrBtnShadow); 
	
	dc.BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), &memDC, 0,0, SRCCOPY);

	memDC.SelectObject(pOldBitmap);
	memDC.DeleteDC();
	bitmap.DeleteObject();	
}

/*********************************************************************
 * Function Name : CTabBarsWnd::SetTabFont
 * Explain : set font
 * Parameter List:
 * CFont *pFont -- 
 * Return:
 * void  -- 
 * Author : orbit 
 * Time : 2002-03-12 17:26:12 
*********************************************************************/
//##ModelId=431C34C70254
void CTabBarsWnd::SetTabFont(CFont *pFont)
{
  
	if(m_pFont != NULL)
	{
		m_pFont->DeleteObject();
		delete m_pFont;
	}
	m_pFont = pFont;
  SetFont(m_pFont, FALSE);
	m_TabCtrl.SetFont(m_pFont,TRUE);

  TEXTMETRIC tm;
  CDC cDC;
  cDC.CreateCompatibleDC(NULL);
  CFont *pOldFont = cDC.SelectObject(m_pFont);
  cDC.GetTextMetrics(&tm);
  m_iMinHeight = tm.tmHeight + tm.tmExternalLeading;
  m_iMinWidth  = tm.tmMaxCharWidth;
  cDC.SelectObject(pOldFont);
	cDC.DeleteDC();
}

// activates and window and restores it's size (i.e. unminimizes it)
// idea and primary implementation: Alexey N. Kirpa (alexeykirpa@mail.ru)
//##ModelId=431C34C702DE
void CTabBarsWnd::ActivateWindow(CComQIPtr<IGenericWindow, &IID_IGenericWindow>& pWindow,BOOL bActive)
{
	DsWindowState  dsWindowState;
	pWindow->get_WindowState(&dsWindowState);
	if (dsWindowState == dsWindowStateMinimized)
	{
		pWindow->put_WindowState(dsWindowStateNormal);
	}
	pWindow->put_Active(bActive ? VARIANT_TRUE : VARIANT_FALSE);
}

// **************************************************************************
// BUG BUG: MFC's _AFXFindPopupMenuFromID won't find submenu text entries.
// Instead of recompiling the MFC dll's, I've placed the following two 
// replacement functions (_FixedAfxFindPopupMenuFromID and OnMeasureItem) 
// here.  They two functions are basically copies of their respectived MFC
// functions - but with the necessery fixes.

static CMenu* AFXAPI _FixedAfxFindPopupMenuFromID(CMenu* pMenu, UINT nID)
{
	ASSERT_VALID(pMenu);
	// walk through all items, looking for ID match
	UINT nItems = pMenu->GetMenuItemCount();
	for (int iItem = 0; iItem < (int)nItems; iItem++)
	{
		CMenu* pPopup = pMenu->GetSubMenu(iItem);
		if (pPopup != NULL)
		{
			if ((UINT)pPopup->GetSafeHmenu() != nID)
			{
    			// recurse to child popup
	    		pPopup = _FixedAfxFindPopupMenuFromID(pPopup, nID);
      }

			if (pPopup != NULL)
				return pPopup;
		}
		else if (pMenu->GetMenuItemID(iItem) == nID)
		{
			// it is a normal item inside our popup
			pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
			return pMenu;
		}
	}
	// not found
	return NULL;
}

// Measure item implementation relies on unique control/menu IDs
//##ModelId=431C34C80148
void CTabBarsWnd::OnMeasureItem(int /*nIDCtl*/, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	if (lpMeasureItemStruct->CtlType == ODT_MENU)
	{
		ASSERT(lpMeasureItemStruct->CtlID == 0);
		CMenu* pMenu;

		_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
		if (pThreadState->m_hTrackingWindow == m_hWnd)
		{
			// start from popup
			pMenu = CMenu::FromHandle(pThreadState->m_hTrackingMenu);
		}
		else
		{
			// start from menubar
			pMenu = GetMenu();
		}

		pMenu = _FixedAfxFindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID);
		if (pMenu != NULL)
			pMenu->MeasureItem(lpMeasureItemStruct);
		else
			TRACE1("Warning: unknown WM_MEASUREITEM for menu item 0x%04X.\n",	lpMeasureItemStruct->itemID);
	}
	else
	{
		CWnd* pChild = GetDescendantWindow(lpMeasureItemStruct->CtlID, TRUE);
		if (pChild != NULL && pChild->SendChildNotifyLastMsg())
			return;     // eaten by child
	}
	// not handled - do default
	Default();
}

// *** ends: MFC bug fix ****************************************************

//##ModelId=431C34C800AB
void CTabBarsWnd::OnTimer(UINT nIDEvent) 
{
	static count = 0;
	if(g_bAutoSave)
	{
		count++;
		if(count > g_nSaveTimeSpan * 6)
		{
			count = 0;
			CComBSTR bStr;
			bStr = _T("FileSaveAll");
			m_pApplication->ExecuteCommand(bStr);

		}
	}
	CWnd::OnTimer(nIDEvent);
}

//##ModelId=431C34C70252
BOOL CTabBarsWnd::DestroyWindow() 
{
	if(m_uTimerID != 0)
		KillTimer(m_uTimerID);
	return CWnd::DestroyWindow();
}
